18.2 代码审查技能

22 分钟阅读

代码审查技能概述#

代码审查技能是 Claude Code Skills 中用于自动化代码审查的重要工具。它可以帮助开发者快速识别代码中的问题,提高代码质量和可维护性。

审查类型#

1. 静态代码分析#

1.1 代码质量检查

python
# src/skills/code_reviewer.py from typing import Dict, Any, List from claude_code_sdk import Skill, SkillContext, SkillResult import re class CodeReviewerSkill(Skill): """代码审查技能""" def __init__(self): super().__init__( name="code-reviewer", version="1.0.0", description="Automated code review skill" ) # 定义审查规则 self.rules = { "naming": self.check_naming_conventions, "complexity": self.check_complexity, "security": self.check_security, "performance": self.check_performance, "documentation": self.check_documentation } def get_parameters_schema(self) -> Dict[str, Any]: return { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to the file to review" }, "code": { "type": "string", "description": "Code to review (alternative to file_path)" }, "language": { "type": "string", "enum": ["python", "javascript", "java", "go"], "description": "Programming language" }, "rules": { "type": "array", "description": "Rules to apply", "items": { "type": "string", "enum": ["naming", "complexity", "security", "performance", "documentation"] } }, "severity": { "type": "string", "enum": ["all", "error", "warning", "info"], "description": "Minimum severity level" } }, "required": ["language"] } def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult: try: language = parameters["language"] rules_to_apply = parameters.get("rules", list(self.rules.keys())) severity = parameters.get("severity", "all") # 获取代码 if "file_path" in parameters: code = context.read_file(parameters["file_path"]) file_path = parameters["file_path"] elif "code" in parameters: code = parameters["code"] file_path = "<inline>" else: return SkillResult( success=False, error="Either file_path or code must be provided" ) # 执行审查 issues = [] for rule_name in rules_to_apply: if rule_name in self.rules: rule_func = self.rules[rule_name] rule_issues = rule_func(code, language) issues.extend(rule_issues) # 过滤严重性 if severity != "all": severity_levels = {"error": 3, "warning": 2, "info": 1} min_level = severity_levels.get(severity, 0) issues = [ issue for issue in issues if severity_levels.get(issue["severity"], 0) >= min_level ] # 生成报告 report = self.generate_report(code, issues, file_path) return SkillResult( success=True, data={ "file_path": file_path, "language": language, "rules_applied": rules_to_apply, "issues": issues, "issue_count": len(issues), "report": report } ) except Exception as e: return SkillResult( success=False, error=str(e) ) def check_naming_conventions(self, code: str, language: str) -> List[Dict]: """检查命名规范""" issues = [] if language == "python": # 检查函数名(应该是 snake_case) func_pattern = r'def\s+([A-Z][a-zA-Z0-9_]*)\s*\(' for match in re.finditer(func_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "naming", "severity": "warning", "line": line_num, "message": f"Function name '{match.group(1)}' should use snake_case", "suggestion": match.group(1).lower() }) # 检查类名(应该是 CamelCase) class_pattern = r'class\s+([a-z][a-zA-Z0-9_]*)\s*[:\(]' for match in re.finditer(class_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "naming", "severity": "warning", "line": line_num, "message": f"Class name '{match.group(1)}' should use CamelCase", "suggestion": match.group(1).title() }) elif language == "javascript": # 检查常量(应该是 UPPER_CASE) const_pattern = r'const\s+([a-z][a-zA-Z0-9_]*)\s*=' for match in re.finditer(const_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "naming", "severity": "info", "line": line_num, "message": f"Constant '{match.group(1)}' should use UPPER_CASE", "suggestion": match.group(1).upper() }) return issues def check_complexity(self, code: str, language: str) -> List[Dict]: """检查复杂度""" issues = [] lines = code.split('\n') for i, line in enumerate(lines, 1): # 计算缩进级别 indent = len(line) - len(line.lstrip()) # 检查过深的嵌套 if indent > 24: issues.append({ "type": "complexity", "severity": "warning", "line": i, "message": f"Deep nesting detected (indent level: {indent // 4})", "suggestion": "Consider refactoring to reduce nesting" }) # 检查长行 if len(line) > 120: issues.append({ "type": "complexity", "severity": "info", "line": i, "message": f"Line too long ({len(line)} characters)", "suggestion": "Break the line into multiple lines" }) return issues ```python def check_security(self, code: str, language: str) -> List[Dict]: """检查安全问题""" issues = [] if language == "python": # 检查 eval 使用 eval_pattern = r'\beval\s*\(' for match in re.finditer(eval_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "security", "severity": "error", "line": line_num, "message": "Use of eval() is dangerous", "suggestion": "Use ast.literal_eval() or alternative safe methods" }) # 检查 exec 使用 exec_pattern = r'\bexec\s*\(' for match in re.finditer(exec_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "security", "severity": "error", "line": line_num, "message": "Use of exec() is dangerous", "suggestion": "Avoid using exec() for security reasons" }) # 检查硬编码密码 password_pattern = r'(password|passwd|pwd)\s*=\s*["\'][^"\']+["\']' for match in re.finditer(password_pattern, code, re.IGNORECASE): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "security", "severity": "warning", "line": line_num, "message": "Hardcoded password detected", "suggestion": "Use environment variables or configuration files" }) elif language == "javascript": # 检查 innerHTML 使用 innerhtml_pattern = r'\.innerHTML\s*=' for match in re.finditer(innerhtml_pattern, code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "security", "severity": "warning", "line": line_num, "message": "Use of innerHTML can lead to XSS vulnerabilities", "suggestion": "Use textContent or sanitize input" }) return issues def check_performance(self, code: str, language: str) -> List[Dict]: """检查性能问题""" issues = [] if language == "python": # 检查循环中的字符串拼接 lines = code.split('\n') in_loop = False loop_indent = 0 for i, line in enumerate(lines, 1): # 检测循环 if re.match(r'\s*(for|while)\s+', line): in_loop = True loop_indent = len(line) - len(line.lstrip()) elif in_loop and len(line) - len(line.lstrip()) <= loop_indent: in_loop = False # 检查字符串拼接 if in_loop and '+=' in line and '"' in line and "'" in line: issues.append({ "type": "performance", "severity": "warning", "line": i, "message": "String concatenation in loop may be inefficient", "suggestion": "Use list and join() for better performance" }) return issues def check_documentation(self, code: str, language: str) -> List[Dict]: """检查文档""" issues = [] if language == "python": # 检查函数是否有文档字符串 func_pattern = r'def\s+(\w+)\s*\([^)]*\):' for match in re.finditer(func_pattern, code): func_name = match.group(1) func_start = match.end()

检查下一行是否有文档字符串

remaining_code = code[func_start:func_start + 100] if not re.search(r'\s*"""', remaining_code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "documentation", "severity": "info", "line": line_num, "message": f"Function '{func_name}' lacks docstring", "suggestion": "Add a docstring to describe the function" })

return issues

def generate_report(self, code: str, issues: List[Dict], file_path: str) -> str: """生成审查报告""" report = f"# Code Review Report for {file_path}\n\n"

统计

error_count = sum(1 for i in issues if i["severity"] == "error") warning_count = sum(1 for i in issues if i["severity"] == "warning") info_count = sum(1 for i in issues if i["severity"] == "info")

report += f"## Summary\n\n" report += f"- Total Issues: {len(issues)}\n" report += f"- Errors: {error_count}\n" report += f"- Warnings: {warning_count}\n" report += f"- Info: {info_count}\n\n"

按类型分组

issues_by_type = {} for issue in issues: issue_type = issue["type"] if issue_type not in issues_by_type: issues_by_type[issue_type] = [] issues_by_type[issue_type].append(issue)

详细问题

for issue_type, type_issues in issues_by_type.items(): report += f"## {issue_type.title()} Issues ({len(type_issues)})\n\n"

for issue in type_issues: severity_icon = { "error": "❌", "warning": "⚠️", "info": "ℹ️" }.get(issue["severity"], "•")

report += f"{severity_icon} Line {issue['line']}: {issue['message']}\n" if "suggestion" in issue: report += f" 💡 Suggestion: {issue['suggestion']}\n" report += "\n"

return report

2. 代码风格检查#

2.1 PEP 8 检查

bash
python

# src/skills/pep8_checker.py
from typing import Dict, Any, List
from claude_code_sdk import Skill, SkillContext, SkillResult
import re

class PEP8CheckerSkill(Skill): """PEP 8 代码风格检查技能"""

bash
def __init__(self):
    super().__init__(
        name="pep8-checker",
        version="1.0.0",
        description="PEP 8 style checker"
    )

def get_parameters_schema(self) -> Dict[str, Any]:
    return {
        "type": "object",
        "properties": {
            "file_path": {
                "type": "string",
                "description": "Path to the file to check"
            },
            "code": {
                "type": "string",
                "description": "Code to check"
            },
            "max_line_length": {
                "type": "integer",
                "description": "Maximum line length",
                "default": 79
            }
        }
    }

def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult:
    try:
        max_line_length = parameters.get("max_line_length", 79)

        # 获取代码
        if "file_path" in parameters:
            code = context.read_file(parameters["file_path"])
            file_path = parameters["file_path"]
        elif "code" in parameters:
            code = parameters["code"]
            file_path = "<inline>"
        else:
            return SkillResult(
                success=False,
                error="Either file_path or code must be provided"
            )

        # 执行检查
        violations = self.check_pep8(code, max_line_length)

        return SkillResult(
            success=True,
            data={
                "file_path": file_path,
                "max_line_length": max_line_length,
                "violations": violations,
                "violation_count": len(violations)
            }
        )
    except Exception as e:
        return SkillResult(
            success=False,
            error=str(e)
        )

def check_pep8(self, code: str, max_line_length: int) -> List[Dict]:
    """检查 PEP 8 规范"""
    violations = []
    lines = code.split('\n')

    for i, line in enumerate(lines, 1):
        # 检查行长度
        if len(line) > max_line_length:
            violations.append({
                "line": i,
                "code": "E501",
                "message": f"Line too long ({len(line)} > {max_line_length} characters)",
                "severity": "warning"
            })

        # 检查尾随空格
        if line.rstrip() != line.rstrip('\n').rstrip('\r'):
            violations.append({
                "line": i,
                "code": "W291",
                "message": "Trailing whitespace",
                "severity": "warning"
            })

        # 检查空行
        if line.strip() == "" and i < len(lines):
            # 检查连续空行
            if i > 1 and lines[i-2].strip() == "" and lines[i-1].strip() == "":
                violations.append({
                    "line": i,
                    "code": "E303",
                    "message": "Too many blank lines",
                    "severity": "info"
                })

        # 检查导入顺序
        if line.strip().startswith("import ") or line.strip().startswith("from "):
            if i > 1 and lines[i-2].strip() and not (
                lines[i-2].strip().startswith("import ") or
                lines[i-2].strip().startswith("from ")
            ):
                violations.append({
                    "line": i,
                    "code": "E402",
                    "message": "Module level import not at top of file",
                    "severity": "error"
                })

    return violations

3. 代码重复检测#

3.1 重复代码检查

python
# src/skills/duplicate_detector.py from typing import Dict, Any, List, Tuple from claude_code_sdk import Skill, SkillContext, SkillResult import difflib class DuplicateDetectorSkill(Skill): """重复代码检测技能""" def __init__(self): super().__init__( name="duplicate-detector", version="1.0.0", description="Detect duplicate code blocks" ) def get_parameters_schema(self) -> Dict[str, Any]: return { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to the file to check" }, "code": { "type": "string", "description": "Code to check" }, "min_lines": { "type": "integer", "description": "Minimum number of lines to consider as duplicate", "default": 5 }, "similarity_threshold": { "type": "number", "description": "Similarity threshold (0.0 to 1.0)", "default": 0.8 } } } def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult: try: min_lines = parameters.get("min_lines", 5) similarity_threshold = parameters.get("similarity_threshold", 0.8) # 获取代码 if "file_path" in parameters: code = context.read_file(parameters["file_path"]) file_path = parameters["file_path"] elif "code" in parameters: code = parameters["code"] file_path = "<inline>" else: return SkillResult( success=False, error="Either file_path or code must be provided" ) # 检测重复 duplicates = self.detect_duplicates(code, min_lines, similarity_threshold) return SkillResult( success=True, data={ "file_path": file_path, "min_lines": min_lines, "similarity_threshold": similarity_threshold, "duplicates": duplicates, "duplicate_count": len(duplicates) } ) except Exception as e: return SkillResult( success=False, error=str(e) ) def detect_duplicates(self, code: str, min_lines: int, similarity_threshold: float) -> List[Dict]: """检测重复代码块""" lines = code.split('\n') duplicates = [] # 提取代码块 blocks = self.extract_blocks(lines, min_lines) # 比较所有块对 for i, block1 in enumerate(blocks): for j, block2 in enumerate(blocks): if i >= j: continue similarity = self.calculate_similarity(block1["code"], block2["code"]) if similarity >= similarity_threshold: duplicates.append({ "block1": { "start_line": block1["start_line"], "end_line": block1["end_line"], "code": block1["code"] }, "block2": { "start_line": block2["start_line"], "end_line": block2["end_line"], "code": block2["code"] }, "similarity": similarity, "suggestion": "Consider extracting common code into a function" }) return duplicates def extract_blocks(self, lines: List[str], min_lines: int) -> List[Dict]: """提取代码块""" blocks = [] for i in range(len(lines) - min_lines + 1): block_code = '\n'.join(lines[i:i+min_lines]) blocks.append({ "start_line": i + 1, "end_line": i + min_lines, "code": block_code }) return blocks def calculate_similarity(self, code1: str, code2: str) -> float: """计算代码相似度""" # 使用序列匹配器 matcher = difflib.SequenceMatcher(None, code1, code2) return matcher.ratio()

使用示例#

1. 完整代码审查#

bash
python

# examples/code_review.py
from skills.code_reviewer import CodeReviewerSkill
from claude_code_sdk import SkillContext

skill = CodeReviewerSkill()
context = SkillContext()

result = skill.execute(
    {
        "file_path": "src/main.py",
        "language": "python",
        "rules": ["naming", "complexity", "security", "performance", "documentation"],
        "severity": "all"
    },
    context
)

print(f"Found {result.data['issue_count']} issues")
print(result.data["report"])

2. PEP 8 检查#

python
# examples/pep8_check.py from skills.pep8_checker import PEP8CheckerSkill from claude_code_sdk import SkillContext skill = PEP8CheckerSkill() context = SkillContext() result = skill.execute( { "file_path": "src/main.py", "max_line_length": 79 }, context ) print(f"Found {result.data['violation_count']} PEP 8 violations") for violation in result.data["violations"]: print(f"Line {violation['line']}: {violation['message']}")

3. 重复代码检测#

bash
python

# examples/duplicate_detection.py
from skills.duplicate_detector import DuplicateDetectorSkill
from claude_code_sdk import SkillContext

skill = DuplicateDetectorSkill()
context = SkillContext()

result = skill.execute(
    {
        "file_path": "src/main.py",
        "min_lines": 5,
        "similarity_threshold": 0.8
    },
    context
)

print(f"Found {result.data['duplicate_count']} duplicate blocks")
for duplicate in result.data["duplicates"]:
    print(f"Lines {duplicate['block1']['start_line']}-{duplicate['block1']['end_line']} "
          f"similar to lines {duplicate['block2']['start_line']}-{duplicate['block2']['end_line']} "
          f"(similarity: {duplicate['similarity']:.2f})")

## 最佳实践

### 1. 审查规则配置

#### 1. 规则优先级
- 安全规则:最高优先级
- 性能规则:高优先级
- 代码质量:中优先级
- 代码风格:低优先级
### 2. 规则定制
- 根据项目需求定制规则
- 考虑团队编码规范
- 逐步引入新规则
### 3. 规则例外
- 提供规则例外机制
- 记录例外原因
- 定期审查例外

2. 审查流程#

bash
markdown

#### 1. 自动审查
- 在提交前自动运行
- 集成到 CI/CD 流程
- 阻止不符合规范的代码

### 2. 人工审查
- 审查自动审查结果
- 关注复杂逻辑
- 提供建设性反馈

### 3. 持续改进
- 收集审查反馈
- 优化审查规则
- 提高审查效率

总结#

代码审查技能可以帮助团队自动化代码审查流程,提高代码质量和一致性。通过合理配置审查规则和流程,可以显著减少代码中的问题,提高开发效率。

在下一节中,我们将探讨文档生成技能。

标记本节教程为已读

记录您的学习进度,方便后续查看。